﻿using System;
using System.Reflection;
using System.Runtime.Serialization;

namespace Octopus.Injection.Commons
{
    public sealed class OctopusTypeBinder: SerializationBinder
    {
        private bool _useOutputAsm;

        public OctopusTypeBinder() :base()
        {
        }

        public OctopusTypeBinder(bool useOutputAsm)
            : base()
        {
            _useOutputAsm = useOutputAsm;
        }

        public override Type BindToType(string assemblyName, string typeName)
        {
            Type typeBase = null;

            if (ConfigRepository.ServiceTypes != null && ConfigRepository.ServiceTypes.ContainsKey(assemblyName))
            {
                if(typeName.Contains("`"))
                {
                    //Es un tipo genérico, cogemos el tipo base y el nº de eltos gen.

                    string[] typeData = typeName.Split('`');
                    int numGenParams = int.Parse(typeData[1].Substring(0, 1));
                    string detGenparams = typeData[1].Substring(1, typeData[1].Length-1);

                    var detGenparamsArr = detGenparams.Replace("[", string.Empty).Split(']');

                    var genTypes = new Type[numGenParams];

                    for (int i = 0; i < numGenParams; i++)
                    {
                        string detGenType = detGenparamsArr[i];
                        var detGenTypeArr = detGenType.Split(',');
                        string genAssemblyName = string.Format("{0}, {1}, {2}, {3}", 
                            detGenTypeArr[1].Trim(), detGenTypeArr[2].Trim(), detGenTypeArr[3].Trim(), detGenTypeArr[4].Trim());

                        if (!_useOutputAsm)
                        {
                            if (ConfigRepository.ServiceTypes.ContainsKey(genAssemblyName))
                            {
                                genTypes[i] = ConfigRepository.ServiceTypes[genAssemblyName].GetType(detGenTypeArr[0]);
                            }
                            else
                            {
                                genTypes[i] = Type.GetType(detGenType);
                            }
                        }
                        else
                        {
                            genTypes[i] = Assembly.Load(genAssemblyName).GetType(detGenTypeArr[0].Trim());
                        }
                    }

                    if(!_useOutputAsm)
                    {
                        typeBase = ConfigRepository.ServiceTypes[assemblyName].GetType(typeData[0] + "`" + typeData[1].Substring(0, 1));
                        typeBase = typeBase.MakeGenericType(genTypes);    
                    }
                    else
                    {
                        typeBase = Assembly.Load(assemblyName).GetType(typeData[0] + "`" + typeData[1].Substring(0, 1));
                        typeBase = typeBase.MakeGenericType(genTypes);  
                    }
                }
                else
                {
                    if (!_useOutputAsm)
                    {
                        typeBase = ConfigRepository.ServiceTypes[assemblyName].GetType(typeName);
                    }
                    else
                    {
                        typeBase = Assembly.Load(assemblyName).GetType(typeName);
                    }
                }
            }
            else
            {
                if(!string.IsNullOrEmpty(assemblyName))
                {
                    typeBase = Type.GetType(string.Format("{0}, {1}", typeName, assemblyName));    
                }
                else
                {
                    typeBase = Type.GetType(typeName);
                }
            }

            return typeBase;
        }
    }
}
